﻿using Cofe.Core.Entry;
using Cofe.Core.Interfaces;
using Cofe.Core.Property;
using Cofe.Core.Utils;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Diagnostics;

namespace Cofe.Core.Implements
{
    [Export(typeof(ICofeService))]
    [ServicePriority(ServicePriorityAttribute.DefaultPriority_COFE)]
    public class CofePathParser : IPathParser
    {
        #region Constructor

        #endregion

        #region Methods

        private async Task<IPropertyHost> parsePathAsync(string fullParsePath, EntryConstructionFlags flags)
        {
            string key, parsePath;

            if (PathFE.ParseFullParseName(fullParsePath, out key, out parsePath))
            {
                if (String.IsNullOrEmpty(key))
                {
                    var foundPh = CofeServices.EntryListRegistrar.Find(parsePath);
                    if (foundPh != null)
                        return foundPh;

                    return null;
                }

                foreach (var dirPH in VolumeRegistrar.Behaviors.GetRootPropertyHosts(flags))
                    if (dirPH.Behaviors.GetProperty<string>(CofeProperties.FullParseName)
                        .Equals(fullParsePath, StringComparison.CurrentCultureIgnoreCase) )
                        return dirPH;

                var vol = VolumeRegistrar.LookupVolume(key);

                if (vol != null)
                {
                    CofeServices.SecurityManager.DemandPermission<IVolumeInfo>(vol, Security.PermissionType.List);

                    var pprovider = await vol.DirectoryLister.TryParsePathAsync(parsePath, flags);
                    CofeServices.Logger.Log("CPP: Parsed and Found {0}", parsePath);
                    if (pprovider != null)
                        return EntryConstructor.ConstructPropertyHost(pprovider);
                }
            }

            if (flags.HasFlag(EntryConstructionFlags.LookupIfParseFailed))
                return await lookupPathAsync(fullParsePath, flags);
            CofeServices.Logger.Log(String.Format("CPP: Parsed and NOT Found {0}, lookup={1}",
                fullParsePath, flags.HasFlag(EntryConstructionFlags.LookupIfParseFailed)), Category.Warn, Priority.Medium);
            return null;
        }        

        private async Task<IPropertyHost> lookupPathAsync(ICofeDirectoryInfo parentDir, string[] pathSplit, EntryConstructionFlags flags)
        {
            if (pathSplit.Length == 0)
                return parentDir.Properties;

            for (int i = 0; i < pathSplit.Length - 1; i++)
            {
                var allEntries = await parentDir.GetCofeSystemInfosAsync(flags);
                var parentEntry = (await parentDir.GetCofeSystemInfosAsync(flags)).FirstOrDefault(
                                e => e.Is<ICofeDirectoryInfo>() &&
                                    e.Name.Equals(pathSplit[i], StringComparison.CurrentCultureIgnoreCase));
                parentDir = parentEntry != null ? parentEntry.As<ICofeDirectoryInfo>() : null;
                if (parentDir == null)
                    return null;
            }
            string fileName = pathSplit[pathSplit.Length - 1];
            ICofeSystemInfo retVal = (await parentDir.GetCofeSystemInfosAsync(flags)).FirstOrDefault(
                e => e.Name.Equals(fileName, StringComparison.CurrentCultureIgnoreCase));       
            return retVal != null ? retVal.Properties : null;
        }

        private Task<IPropertyHost> lookupPathAsync(ICofeDirectoryInfo parentDir, string relativePath, EntryConstructionFlags flags)
        {            
            string[] pathSplit = relativePath.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries);
            return lookupPathAsync(parentDir, pathSplit, flags);
        }

        private async Task<IPropertyHost> lookupPathAsync(string fullParsePath, EntryConstructionFlags flags)
        {
            string startPath = fullParsePath;
            IPropertyHost foundEntry = null;
            EntryConstructionFlags parseFlags = flags & ~EntryConstructionFlags.LookupIfParseFailed; //http://stackoverflow.com/questions/4778166/how-to-remove-an-item-for-a-ord-enum
            
            while (!(String.IsNullOrEmpty(startPath)) && foundEntry == null)
            {
                foundEntry = await CofeServices.PathParserManager.Behaviors
                    .TryParsePathAsync(startPath, ParsableType.ParsePath, parseFlags);
                if (foundEntry == null)
                    startPath = PathFE.GetDirectoryName(startPath);
            }

            if (String.IsNullOrEmpty(startPath))
                return null;

            ICofeDirectoryInfo startDir = CofeServices.EntryConstructor.ConstructEntry<ICofeDirectoryInfo>(foundEntry);
            return await lookupPathAsync(startDir, fullParsePath.Substring(startPath.Length + 1), flags);

            //foreach (var dir in VolumeFE.RootDirectories)
            //{
            //    if (fullParsePath.StartsWith(dir.ParseName + "\\", StringComparison.CurrentCultureIgnoreCase))
            //    {
            //        string relativePath = fullParsePath.Substring(dir.ParseName.Length + 1);
            //        var retVal = await lookupPathAsync(dir, relativePath, flags);
            //        if (retVal != null)
            //            return retVal;
            //    }
            //}
            return null;
        }

        public async Task<IPropertyHost> TryParsePathAsync(string fullParsePath, ParsableType type,
            EntryConstructionFlags flags = EntryConstructionFlags.Default)
        {
            switch (type)
            {
                case ParsableType.ParsePath:
                case ParsableType.ParsePathLookup:
                    if (string.IsNullOrEmpty(fullParsePath))
                        return CofeServices.EntryConstructor.ConstructPropertyHost(PropertyProvider.ForUserRootDirectory());
                    return await parsePathAsync(fullParsePath, flags);
                case ParsableType.FilterString:
                    return EntryConstructor.ConstructPropertyHost(new AutoEntryListPropertyProvider(fullParsePath));
                default:
                    throw new NotSupportedException();
            }

        }


        public async Task<bool> ExistsAsync(string fullParsePath, ParsableType type)
        {
            if (type == ParsableType.FilterString)
                return true;

            var ph = await TryParsePathAsync(fullParsePath, type);
            return ph != null;
        }

        public IPropertyHost TryParsePath(string fullParsePath, ParsableType type,
           EntryConstructionFlags flags = EntryConstructionFlags.Default)
        {
            return TryParsePathAsync(fullParsePath, type, flags).Result;
        }

        #endregion

        #region Data

        private IVolumeRegistrar _volumeRegistrar = null;
        private IEntryConstructor _entryConstructor = null;

        #endregion

        #region Public Properties

        public ParsableType SupportedParseType
        {
            get { return ParsableType.ParsePath | ParsableType.ParsePathLookup | ParsableType.FilterString; }
        }


        public IVolumeRegistrar VolumeRegistrar
        {
            get { return CofeServices.VolumeRegistrar; }
            set { _volumeRegistrar = value; }
        }

        public IEntryConstructor EntryConstructor
        {
            get { return CofeServices.EntryConstructor; }
            set { _entryConstructor = value; }
        }

        #endregion






    }
}
